# recommend_next_week_final.py - 修复股价获取问题
import os
import pandas as pd
from typing import List, Optional, Tuple, Dict
import qlib
from qlib.workflow import R
from qlib.data import D

def _get_latest_recorder(experiment_name: str = "workflow"):
    exp = R.get_exp(experiment_name=experiment_name)
    recorders = exp.list_recorders()
    if not recorders:
        raise RuntimeError(f"No recorders found under experiment '{experiment_name}'.")
    rid = sorted(recorders.keys())[-1]
    return exp, recorders[rid], rid

def _load_model_and_dataset(experiment_name: str = "workflow", recorder_id: Optional[str] = None):
    if recorder_id is None:
        _, recorder, _ = _get_latest_recorder(experiment_name)
    else:
        recorder = R.get_recorder(experiment_name=experiment_name, recorder_id=recorder_id)

    try:
        model = recorder.load_object("trained_model")
    except Exception:
        model = recorder.load_object("params.pkl")
    dataset = recorder.load_object("dataset")
    return model, dataset, recorder

def _next_trading_day(from_date: pd.Timestamp) -> pd.Timestamp:
    cal = D.calendar(start_time=from_date, end_time=from_date + pd.Timedelta(days=30), freq="day")
    cal = pd.DatetimeIndex(cal)
    next_trading_days = cal[cal > from_date]
    
    if len(next_trading_days) == 0:
        if len(cal) > 0:
            last_available = cal[-1]
            print(f"Warning: No future trading days found. Using last available: {last_available.date()}")
            return last_available
        else:
            raise ValueError(f"No trading days found after {from_date}")
    
    return next_trading_days[0]

def _fix_mlflow_uri(mlflow_uri: str) -> str:
    """修复MLflow URI格式"""
    if not mlflow_uri.startswith('file://'):
        # 更安全的路径处理
        fixed_uri = mlflow_uri.replace('\\', '/')
        # 处理Windows路径
        if ':' in fixed_uri:
            # 将 E:/path 转换为 file:///E:/path
            fixed_uri = f"file:///{fixed_uri}"
        else:
            fixed_uri = f"file://{fixed_uri}"
    return fixed_uri

def get_next_week_recommendations(
    provider_uri: str,
    region: str = "cn",
    experiment_name: str = "workflow",
    recorder_id: Optional[str] = None,
    mlflow_uri: Optional[str] = None,
    investment_amount: float = 40000,
    target_stocks: int = 8,
    manual_test_date: Optional[str] = None,  # 新增：手动测试日期
    workflow_config: Optional[Dict] = None, # 新增：workflow配置
) -> Tuple[pd.Timestamp, List[str], pd.Series, dict]:
    
    qlib.init(provider_uri=provider_uri, region=region, expression_cache=None, dataset_cache=None)

    # 修复MLflow URI格式问题
    if mlflow_uri:
        fixed_uri = _fix_mlflow_uri(mlflow_uri)
        print(f"Setting MLflow URI: {fixed_uri}")
        try:
            R.set_uri(fixed_uri)
        except Exception as e:
            print(f"Warning: Failed to set MLflow URI: {e}")
            print("Trying to continue without setting URI...")

    # 尝试加载模型和数据集
    try:
        model, dataset, recorder = _load_model_and_dataset(experiment_name=experiment_name, recorder_id=recorder_id)
    except Exception as e:
        print(f"Failed to load model from MLflow: {e}")
        raise ValueError("Cannot load model. Please check the training directory.")

    # 获取推荐时间范围
    if manual_test_date:
        # 手动测试模式：使用指定的日期作为基准
        try:
            base_date = pd.Timestamp(manual_test_date)
            print(f"\n🧪 手动测试模式已启用")
            print(f"   测试基准日期: {base_date.strftime('%Y-%m-%d')} ({base_date.strftime('%A')})")
            
            # 计算该日期所在周的下周范围
            week_start = base_date - pd.Timedelta(days=base_date.weekday())  # 该周周一
            next_week_start_date = week_start + pd.Timedelta(days=7)         # 下周周一
            next_week_end_date = next_week_start_date + pd.Timedelta(days=4) # 下周周五（5个交易日）
            
            today = base_date  # 用于后续计算
            next_week_start = next_week_start_date
            
        except Exception as e:
            print(f"❌ 手动测试日期格式错误: {e}")
            print(f"   请使用 YYYY-MM-DD 格式，例如: 2025-08-22")
            raise ValueError(f"Invalid manual test date: {manual_test_date}")
    else:
        # 自动模式：基于当前日期计算下周
        today = pd.Timestamp.today().normalize()
        next_week_start = today + pd.Timedelta(days=7)
        
        # 计算推荐时间范围
        current_week_start = today - pd.Timedelta(days=today.weekday())  # 本周一
        current_week_end = current_week_start + pd.Timedelta(days=6)     # 本周日
        next_week_start_date = current_week_start + pd.Timedelta(days=7) # 下周一
        next_week_end_date = next_week_start_date + pd.Timedelta(days=6) # 下周日
    
    print(f"\n📅 推荐时间范围说明:")
    if manual_test_date:
        print(f"   测试基准日期: {today.strftime('%Y-%m-%d')} ({today.strftime('%A')})")
        print(f"   推荐范围: {next_week_start_date.strftime('%Y-%m-%d')} 至 {next_week_end_date.strftime('%Y-%m-%d')} (5个交易日)")
        print(f"   推荐目的: 手动测试模式 - 验证模型在指定日期的预测准确性")
    else:
        print(f"   当前日期: {today.strftime('%Y-%m-%d')} ({today.strftime('%A')})")
        print(f"   本周范围: {current_week_start.strftime('%Y-%m-%d')} 至 {current_week_end.strftime('%Y-%m-%d')}")
        print(f"   推荐范围: {next_week_start_date.strftime('%Y-%m-%d')} 至 {next_week_end_date.strftime('%Y-%m-%d')}")
        print(f"   推荐目的: 为下周交易做准备，基于当前模型评分选择股票")
    
    try:
        trade_dt = _next_trading_day(next_week_start)
        print(f"   目标交易日: {trade_dt.date()} ({trade_dt.strftime('%A')})")
    except ValueError:
        cal = D.calendar(start_time=today - pd.Timedelta(days=30), end_time=today, freq="day")
        cal = pd.DatetimeIndex(cal)
        if len(cal) > 0:
            trade_dt = cal[-1]
            print(f"   使用最后可用交易日: {trade_dt.date()}")
        else:
            raise ValueError("No trading days available")

    # 加载预测结果
    try:
        print("Loading prediction results...")
        pred_file = recorder.load_object("pred.pkl")
        print(f"Successfully loaded prediction file with shape: {pred_file.shape}")
        
        # 获取可用的日期
        available_dates = pred_file.index.get_level_values('datetime').unique()
        # print(f"Available dates: {[d.date() for d in available_dates]}")
        
        # 选择最接近目标日期的可用日期
        if trade_dt in available_dates:
            target_date = trade_dt
        else:
            date_diff = [(abs((d - trade_dt).days), d) for d in available_dates]
            target_date = min(date_diff, key=lambda x: x[0])[1]
            print(f"Target date {trade_dt.date()} not available, using closest: {target_date.date()}")
        
        # 提取该日期的预测结果
        pred_day = pred_file.xs(target_date, level="datetime")
        if hasattr(pred_day, 'columns') and len(pred_day.columns) > 0:
            pred_day = pred_day.iloc[:, 0]
        pred_day.name = "score"
        
        # 排序并获取推荐股票
        sorted_pred = pred_day.sort_values(ascending=False)
        top_stocks = sorted_pred.head(target_stocks).index.tolist()
        
        # 生成投资计划
        investment_plan = _generate_investment_plan(
            top_stocks, sorted_pred, investment_amount, target_stocks, target_date, workflow_config
        )
        
        return target_date, top_stocks, sorted_pred, investment_plan
        
    except Exception as e:
        print(f"Failed to load predictions: {e}")
        raise ValueError("Cannot load predictions. Please check your model training.")

def _get_stock_price_accurate(stock_code: str, date: pd.Timestamp) -> float:
    """获取准确的股票价格 - 修复版本"""
    try:
        # 方法1：获取指定日期及之前的最新价格
        end_date = date
        start_date = date - pd.Timedelta(days=30)  # 往前找30天
        
        # 获取收盘价数据
        stock_data = D.features([stock_code], ['$close'], start_time=start_date, end_time=end_date, freq='day')
        
        if not stock_data.empty:
            # 取最后一个非空的价格
            latest_price = stock_data.iloc[:, 0].dropna().iloc[-1]
            print(f"✅ {stock_code} 价格获取成功: ¥{latest_price:.2f}")
            return latest_price
        
        # 方法2：如果指定日期没有数据，获取最近有数据的日期
        print(f"⚠️  {stock_code} 在 {date.date()} 没有数据，尝试获取最近价格...")
        
        # 获取最近60天的数据
        start_date = date - pd.Timedelta(days=60)
        stock_data = D.features([stock_code], ['$close'], start_time=start_date, end_time=date, freq='day')
        
        if not stock_data.empty:
            latest_price = stock_data.iloc[:, 0].dropna().iloc[-1]
            print(f"✅ {stock_code} 获取到最近价格: ¥{latest_price:.2f}")
            return latest_price
        
        # 方法3：如果还是没有，尝试获取最新可用数据
        print(f"⚠️  {stock_code} 在最近60天没有数据，尝试获取最新可用数据...")
        
        # 获取最近一年的数据
        start_date = date - pd.Timedelta(days=365)
        stock_data = D.features([stock_code], ['$close'], start_time=start_date, end_time=date, freq='day')
        
        if not stock_data.empty:
            latest_price = stock_data.iloc[:, 0].dropna().iloc[-1]
            print(f"✅ {stock_code} 获取到最新可用价格: ¥{latest_price:.2f}")
            return latest_price
        
        # 方法4：如果所有方法都失败，使用默认价格并警告
        print(f"❌ {stock_code} 无法获取价格数据，使用默认价格 ¥50.00")
        return 50.0
        
    except Exception as e:
        print(f"❌ 获取 {stock_code} 价格时出错: {e}")
        return 50.0

def _get_stock_price_with_verification(stock_code: str, date: pd.Timestamp) -> float:
    """获取股票价格并进行验证 - 更可靠的版本"""
    
    # 首先尝试获取价格
    price = _get_stock_price_accurate(stock_code, date)
    
    # 验证价格是否合理
    if price < 1 or price > 10000:
        print(f"⚠️  {stock_code} 价格 {price:.2f} 可能不合理，尝试重新获取...")
        
        # 尝试获取更多历史数据来验证
        try:
            start_date = date - pd.Timedelta(days=90)
            stock_data = D.features([stock_code], ['$close', '$open', '$high', '$low'], 
                                  start_time=start_date, end_time=date, freq='day')
            
            if not stock_data.empty:
                # 使用多个价格指标来验证
                close_prices = stock_data.iloc[:, 0].dropna()
                open_prices = stock_data.iloc[:, 1].dropna()
                high_prices = stock_data.iloc[:, 2].dropna()
                low_prices = stock_data.iloc[:, 3].dropna()
                
                if len(close_prices) > 0:
                    # 取最近的价格
                    verified_price = close_prices.iloc[-1]
                    
                    # 检查价格是否在合理范围内
                    if 1 <= verified_price <= 10000:
                        print(f"✅ {stock_code} 价格验证成功: ¥{verified_price:.2f}")
                        return verified_price
                    else:
                        print(f"⚠️  {stock_code} 验证后价格仍不合理: ¥{verified_price:.2f}")
        
        except Exception as e:
            print(f"❌ {stock_code} 价格验证失败: {e}")
    
    return price

def _filter_stocks_by_quality(
    top_stocks: List[str], 
    sorted_scores: pd.Series,
    target_date: pd.Timestamp,
    max_stocks: int = 8
) -> Tuple[List[str], pd.Series]:
    """基于质量指标筛选股票，避免后视偏差"""
    
    print(f"\n🔍 股票质量筛选（避免后视偏差）...")
    
    # 获取每只股票的质量指标
    stock_quality = {}
    
    for stock in top_stocks[:max_stocks*2]:  # 从更多股票中筛选
        try:
            # 获取最近的价格数据
            end_date = target_date
            start_date = target_date - pd.Timedelta(days=60)  # 最近60天
            
            # 获取价格和成交量数据
            features = ['$close', '$volume', '$turnover_rate']
            stock_data = D.features([stock], features, start_time=start_date, end_time=end_date, freq='day')
            
            if stock_data.empty:
                continue
                
            # 计算质量指标
            close_prices = stock_data.iloc[:, 0]
            volumes = stock_data.iloc[:, 1]
            turnover_rates = stock_data.iloc[:, 2]
            
            # 1. 价格稳定性（避免暴涨暴跌）
            price_volatility = close_prices.pct_change().std()
            
            # 2. 成交量稳定性
            volume_stability = volumes.pct_change().std()
            
            # 3. 换手率合理性（避免过度炒作）
            avg_turnover = turnover_rates.mean()
            
            # 4. 最近价格趋势（避免追高）
            recent_trend = (close_prices.iloc[-1] / close_prices.iloc[-5] - 1) if len(close_prices) >= 5 else 0
            
            # 综合质量评分（越低越好）
            quality_score = (
                price_volatility * 100 +  # 价格波动
                volume_stability * 10 +   # 成交量波动
                abs(avg_turnover - 3) +   # 换手率偏离3%的程度
                abs(recent_trend) * 50    # 最近涨幅偏离0%的程度
            )
            
            stock_quality[stock] = {
                'quality_score': quality_score,
                'price_volatility': price_volatility,
                'volume_stability': volume_stability,
                'avg_turnover': avg_turnover,
                'recent_trend': recent_trend,
                'model_score': sorted_scores[stock]
            }
            
        except Exception as e:
            print(f"Warning: 无法分析 {stock} 的质量指标: {e}")
            continue
    
    # 按质量评分排序（质量好的在前）
    sorted_quality = sorted(stock_quality.items(), key=lambda x: x[1]['quality_score'])
    
    # 选择质量最好的股票
    selected_stocks = [stock for stock, _ in sorted_quality[:max_stocks]]
    
    print(f"✅ 筛选完成，选择了 {len(selected_stocks)} 只质量较好的股票")
    
    # 返回筛选后的股票和评分
    filtered_scores = sorted_scores[selected_stocks]
    
    return selected_stocks, filtered_scores

def _generate_investment_plan(
    top_stocks: List[str], 
    sorted_scores: pd.Series, 
    investment_amount: float,
    target_stocks: int,
    target_date: pd.Timestamp,
    workflow_config: Optional[Dict] = None
) -> dict:
    
    # 使用质量筛选后的股票
    selected_stocks, filtered_scores = _filter_stocks_by_quality(
        top_stocks, sorted_scores, target_date, target_stocks
    )
    
    # 重新计算权重
    total_score = filtered_scores.sum()
    weights = (filtered_scores / total_score).round(4)
    
    # 计算每只股票的实际投资方案
    stock_amounts = {}
    total_actual_amount = 0
    
    # 计算推荐有效期
    today = pd.Timestamp.today().normalize()
    next_week_start = today + pd.Timedelta(days=7)
    next_week_end = next_week_start + pd.Timedelta(days=6)
    
    print(f"\n💰 计算投资方案...")
    print(f"📅 推荐有效期: {next_week_start.strftime('%Y-%m-%d')} 至 {next_week_end.strftime('%Y-%m-%d')}")
    print(f"🎯 建议买入时机: 下周一开盘前或开盘后30分钟内")
    print(f"⏰ 模型评分基准日: {target_date.strftime('%Y-%m-%d')}")
    
    # 使用workflow配置参数
    if workflow_config:
        risk_degree = workflow_config.get('risk_degree', 0.9)
        hold_thresh = workflow_config.get('hold_thresh', 5)
        label_days = workflow_config.get('label_days', 7)
        print(f"🧪 测试说明: 这是手动测试模式，用于验证模型预测准确性")
        print(f"📊 策略配置: 风险敞口{risk_degree*100}%, 最少持有{hold_thresh}天, 预测{label_days}日收益")
    else:
        risk_degree = 0.9
        hold_thresh = 5
        label_days = 7
    
    # for i, (stock, weight) in enumerate(weights.items(), 1):
    #     print(f"\n📊 分析第 {i} 只股票: {stock}")
        
    #     # 获取准确价格
    #     stock_price = _get_stock_price_with_verification(stock, target_date)
        
    #     # 计算投资方案
    #     theoretical_amount = investment_amount * weight
    #     shares_100 = max(100, int(theoretical_amount / stock_price / 100) * 100)
    #     actual_amount = shares_100 * stock_price
        
    #     print(f"   理论投资: ¥{theoretical_amount:,.0f}")
    #     print(f"   股票价格: ¥{stock_price:.2f}")
    #     print(f"   购买股数: {shares_100}")
    #     print(f"   实际投资: ¥{actual_amount:,.0f}")
        
    #     # 展示历史数据
    #     # show_stock_history(stock)
        
    #     # 生成推荐理由
    #     reason = _generate_stock_reason_improved(stock, filtered_scores[stock], i, stock_price)
        
    #     stock_amounts[stock] = {
    #         'rank': i,
    #         'score': f"{filtered_scores[stock]:.4f}",
    #         'weight': f"{weight*100:.2f}%",
    #         'stock_price': f"¥{stock_price:.2f}",
    #         'shares': shares_100,
    #         'theoretical_amount': f"¥{theoretical_amount:,.0f}",
    #         'actual_amount': f"¥{actual_amount:,.0f}",
    #         'reason': reason
    #     }
        
    #     total_actual_amount += actual_amount
    
    print(f"\n💰 总投资计算完成: ¥{total_actual_amount:,.0f}")
    
    # 生成投资建议
    plan = {
        'total_investment': f"¥{investment_amount:,.0f}",
        'total_actual_amount': f"¥{total_actual_amount:,.0f}",
        'recommended_stocks': len(selected_stocks),
        'suggested_holdings': min(workflow_config.get('topk', 8) if workflow_config else 8, len(selected_stocks)),
        'stock_details': stock_amounts,
        'risk_control': {
            'max_single_stock': f"¥{investment_amount * (workflow_config.get('risk_degree', 0.9) if workflow_config else 0.9):,.0f}",
            'suggested_batch_buy': f"建议分2批买入，每批间隔{workflow_config.get('hold_thresh', 5) if workflow_config else 5}-7天",
            'stop_loss': "单只股票跌幅超过10%立即卖出",
            'take_profit': "单只股票涨幅超过20%考虑减仓",
            'risk_degree': f"{workflow_config.get('risk_degree', 0.9)*100:.0f}%" if workflow_config else "90%",
            'hold_thresh': f"{workflow_config.get('hold_thresh', 5)}天" if workflow_config else "5天"
        },
        'weekly_rebalance': f"建议每{workflow_config.get('hold_thresh', 5) if workflow_config else 5}天检查一次，每{workflow_config.get('hold_thresh', 5)*2 if workflow_config else 10}天调整1-2只股票",
        'investment_notes': [
            "已通过质量筛选，避免后视偏差",
            f"推荐{workflow_config.get('topk', 8) if workflow_config else 8}只，建议选择{workflow_config.get('topk', 8) if workflow_config else 8}只集中投资",
            "金额已考虑100股起卖限制",
            "优先选择排名前5的股票",
            "新手建议分批买入，控制风险",
            f"风险敞口: {workflow_config.get('risk_degree', 0.9)*100:.0f}%" if workflow_config else "风险敞口: 90%",
            f"最少持有: {workflow_config.get('hold_thresh', 5)}天" if workflow_config else "最少持有: 5天",
            f"预测目标: {workflow_config.get('label_days', 7)}日收益率" if workflow_config else "预测目标: 7日收益率"
        ]
    }
    
    return plan

def show_stock_history(stock_code: str, days: int = 90) -> None:
    """展示股票历史数据图表
    
    Args:
        stock_code: 股票代码
        days: 要展示的历史天数
    """
    import matplotlib.pyplot as plt
    
    end_date = pd.Timestamp.today()
    start_date = end_date - pd.Timedelta(days=days)
    
    # 获取历史数据
    data = D.features([stock_code], 
                     ['$open','$close','$high','$low','$volume'],
                     start_time=start_date, end_time=end_date, freq='day')
    
    if data.empty:
        print(f"⚠️  {stock_code} 在近{days}天没有数据")
        return
    
    try:
        # 数据验证和处理
        assert '$close' in data.columns, "缺少收盘价列"
        assert '$low' in data.columns, "缺少最低价列"
        assert '$high' in data.columns, "缺少最高价列"
        assert '$volume' in data.columns, "缺少成交量列"
        
        # 处理多级索引
        if isinstance(data.index, pd.MultiIndex):
            data = data.droplevel(0)
            
        # 确保索引是日期类型
        data.index = pd.to_datetime(data.index)
        
    except Exception as e:
        print(f"⚠️ 数据格式错误: {e}")
        print(f"实际数据列: {data.columns.tolist()}")
        print(f"索引类型: {type(data.index)}")
        return
    
    # 绘制图表
    plt.figure(figsize=(12,6))
    plt.plot(data['$close'], label='close_price')
    plt.fill_between(data.index, data['$low'], data['$high'], 
                    alpha=0.2, color='gray', label='price_range')
    plt.bar(data.index, data['$volume'], alpha=0.3, color='blue', label='volume')
    
    plt.title(f'{stock_code} Last {days} Days History')
    plt.xlabel('Date')
    plt.ylabel('Price/Volume')
    
    # Add model reasoning explanation
    score = filtered_scores.get(stock_code, 0) if 'filtered_scores' in globals() else 0
    rank = i if 'i' in globals() else 'N/A'
    
    trend = data['$close'].pct_change().mean()
    trend_explanation = "showing strong upward momentum" if trend > 0.005 else "showing moderate growth" if trend > 0 else "showing downward pressure"
    
    volatility = data['$close'].std()
    vol_explanation = "(high volatility, potential for big moves)" if volatility > data['$close'].mean()*0.02 else "(stable price movements)"
    
    volume_change = data['$volume'].pct_change().mean()
    volume_explanation = "with increasing trading interest" if volume_change > 0.1 else "with steady trading volume"
    
    model_reason = f"""
    📊 Model Reasoning:
    - Score: {score:.4f} (confidence level)
    - Rank: #{rank} among candidates
    - Key Metrics:
      • Recent Trend: {trend:.2%} {trend_explanation}
      • Volatility: {volatility:.2f} {vol_explanation}
      • Volume Change: {volume_change:.2%} {volume_explanation}
    """
    
    # Add model reason text to plot
    plt.figtext(0.5, 0.02, model_reason, ha='center', va='bottom', 
               bbox=dict(facecolor='white', alpha=0.5), wrap=True)
    
    plt.figtext(0.5, -0.15, model_reason, ha='center', va='top', fontsize=10, 
               bbox={'facecolor':'white', 'alpha':0.8, 'pad':5})
    
    plt.subplots_adjust(bottom=0.3)
    plt.legend()
    plt.grid()
    plt.show()


def _generate_stock_reason_improved(stock: str, score: float, rank: int, price: float) -> str:
    """生成改进的推荐理由"""
    
    # 基于评分和排名生成推荐理由
    if score > 0.8:
        score_level = "非常优秀"
    elif score > 0.6:
        score_level = "优秀"
    elif score > 0.4:
        score_level = "良好"
    else:
        score_level = "一般"
    
    if rank <= 3:
        rank_desc = "排名前列"
    elif rank <= 5:
        rank_desc = "排名靠前"
    else:
        rank_desc = "排名中等"
    
    # 价格区间判断
    if price < 20:
        price_desc = "低价股"
    elif price < 50:
        price_desc = "中价股"
    else:
        price_desc = "高价股"
    
    reasons = [
        f"AI模型评分{score_level}（{score:.4f}）",
        f"在推荐股票中{rank_desc}（第{rank}名）",
        f"{price_desc}，当前价格¥{price:.2f}",
        "已通过质量筛选，避免后视偏差",
        "综合考虑技术指标、基本面和市场表现"
    ]
    
    return "；".join(reasons)

def print_investment_recommendation(plan: dict, manual_test_date: Optional[str] = None):
    if manual_test_date:
        print("\n" + "="*80)
        print("🧪 手动测试模式 - 投资推荐计划")
        print("="*80)
        print(f"📅 测试基准日期: {manual_test_date}")
    else:
        print("\n" + "="*80)
        print("🎯 下周投资推荐计划（新手友好版）")
        print("="*80)
    
    print(f"\n💰 计划投资金额: {plan['total_investment']}")
    print(f"💵 实际投资金额: {plan['total_actual_amount']}")
    print(f"📈 推荐股票数量: {plan['recommended_stocks']}只")
    print(f"🎯 建议持仓数量: {plan['suggested_holdings']}只")
    
    # 显示workflow配置信息
    if 'risk_control' in plan and 'risk_degree' in plan['risk_control']:
        print(f"⚙️  策略配置: 风险敞口{plan['risk_control']['risk_degree']}, 最少持有{plan['risk_control']['hold_thresh']}")
    
    print(f"\n📊 股票详细配置（推荐{plan['recommended_stocks']}选{plan['suggested_holdings']}）:")
    print("-" * 80)
    print(f"{'排名':<4} {'股票代码':<10} {'评分':<8} {'权重':<8} {'股价':<8} {'股数':<8} {'实际金额':<12} {'推荐理由'}")
    print("-" * 80)
    
    for stock, details in plan['stock_details'].items():
        print(f"{details['rank']:<4} {stock:<10} {details['score']:<8} {details['weight']:<8} "
              f"{details['stock_price']:<8} {details['shares']:<8} {details['actual_amount']:<12} "
              f"{details['reason']}")
    
    print(f"\n⚠️  风险控制:")
    print(f"    • 单只股票最大仓位: {plan['risk_control']['max_single_stock']}")
    print(f"    • 买入策略: {plan['risk_control']['suggested_batch_buy']}")
    print(f"    • 止损线: {plan['risk_control']['stop_loss']}")
    print(f"    • 止盈线: {plan['risk_control']['take_profit']}")
    
    print(f"\n🔄 调仓建议:")
    print(f"    • 调仓频率: {plan['weekly_rebalance']}")
    
    print(f"\n📝 投资说明:")
    for note in plan['investment_notes']:
        print(f"    • {note}")
    
    print(f"\n🎯 新手投资建议:")
    print(f"    1. 从推荐的{plan['recommended_stocks']}只股票中选择{plan['suggested_holdings']}只")
    print(f"    2. 优先选择排名前{min(5, plan['suggested_holdings'])}的股票")
    print(f"    3. 分2批买入，每批间隔{plan['risk_control'].get('hold_thresh', '5-7天')}")
    print(f"    4. 设置10%止损，20%止盈")
    print(f"    5. 每{plan['risk_control'].get('hold_thresh', '5天')}检查一次，每{plan['risk_control'].get('hold_thresh', '10天')}调整1-2只")
    
    print("\n" + "="*80)

if __name__ == "__main__":
    # 配置参数
    provider_uri = os.path.expanduser("~/.qlib/qlib_data/cn_data")
    mlflow_uri = r"E:\study\AI-Quant-Trade\QLIB\my-qlib\mlruns"
    
    print(f"Provider URI: {provider_uri}")
    print(f"MLflow URI: {mlflow_uri}")
    
    # 生成下周投资推荐
    try:
        # 手动测试模式：可以修改这里的日期来测试不同时间点的预测
        manual_test_date = "2025-08-15"  # 修改这里来测试不同日期，例如: "2025-08-22"
        
        # 配置参数 - 与当前workflow保持一致
        workflow_config = {
            'topk': 8,                    # 与workflow中的strategy.kwargs.topk一致
            'n_drop': 1,                  # 与workflow中的strategy.kwargs.n_drop一致
            'hold_thresh': 5,             # 与workflow中的strategy.kwargs.hold_thresh一致
            'risk_degree': 0.9,           # 与workflow中的strategy.kwargs.risk_degree一致
            'only_tradable': True,        # 与workflow中的strategy.kwargs.only_tradable一致
            'label_days': 7,              # 与workflow中的label: Ref($close, -7)一致
            'account': 1000000,           # 与workflow中的backtest.account一致
            'open_cost': 0.0005,          # 与workflow中的exchange_kwargs.open_cost一致
            'close_cost': 0.0015,         # 与workflow中的exchange_kwargs.close_cost一致
            'min_cost': 5,                # 与workflow中的exchange_kwargs.min_cost一致
            'limit_threshold': 0.095      # 与workflow中的exchange_kwargs.limit_threshold一致
        }
        
        print(f"🔧 当前配置 (与workflow保持一致):")
        print(f"   • 手动测试日期: {manual_test_date}")
        print(f"   • 投资金额: ¥{workflow_config['account']}")
        print(f"   • 目标股票数: {workflow_config['topk']}只")
        print(f"   • 策略参数: topk={workflow_config['topk']}, n_drop={workflow_config['n_drop']}, hold_thresh={workflow_config['hold_thresh']}")
        print(f"   • 风险控制: risk_degree={workflow_config['risk_degree']}, only_tradable={workflow_config['only_tradable']}")
        print(f"   • 交易成本: 开仓{workflow_config['open_cost']*100}%, 平仓{workflow_config['close_cost']*100}%, 最低{workflow_config['min_cost']}元")
        print(f"   • 涨跌停限制: {workflow_config['limit_threshold']*100}%")
        
        trade_dt, top_stocks, ranked_scores, investment_plan = get_next_week_recommendations(
            provider_uri=provider_uri,
            region="cn",
            experiment_name="workflow",
            recorder_id=None,
            mlflow_uri=mlflow_uri,
            investment_amount=workflow_config['account'],
            target_stocks=workflow_config['topk'],
            manual_test_date=manual_test_date,  # 传入手动测试日期
            workflow_config=workflow_config,    # 传入workflow配置
        )
        
        # 打印推荐结果
        print_investment_recommendation(investment_plan, manual_test_date)
        
        # 保存推荐结果到文件
        output_file = f"investment_recommendation_{trade_dt.date()}.txt"
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(f"投资推荐日期: {trade_dt.date()}\n")
            f.write(f"推荐股票: {', '.join(top_stocks)}\n")
            f.write(f"总投资金额: {investment_plan['total_investment']}\n")
            f.write(f"建议选择: 从{workflow_config['topk']}只中选{workflow_config['topk']}只集中投资\n")
        
        print(f"\n💾 推荐结果已保存到: {output_file}")
        
        # 添加手动测试说明
        if manual_test_date:
            print(f"\n🧪 手动测试模式使用说明:")
            print(f"   1. 当前测试基准日: {manual_test_date}")
            print(f"   2. 推荐股票范围: 下周一到周五 (5个交易日)")
            print(f"   3. 验证方法: 记录推荐股票，观察下周实际涨跌幅")
            print(f"   4. 修改测试日期: 在代码中修改 manual_test_date 变量")
            print(f"   5. 建议测试多个历史日期，评估模型稳定性")
            print(f"   6. 当前策略配置: topk={workflow_config['topk']}, n_drop={workflow_config['n_drop']}, hold_thresh={workflow_config['hold_thresh']}")
            print(f"   7. 风险控制: risk_degree={workflow_config['risk_degree']}, only_tradable={workflow_config['only_tradable']}")
            print(f"   8. 预测目标: {workflow_config['label_days']}日收益率")
        
        # 添加时间范围总结
        if manual_test_date:
            # 手动测试模式
            # 重新计算日期范围用于总结
            base_date_summary = pd.Timestamp(manual_test_date)
            week_start_summary = base_date_summary - pd.Timedelta(days=base_date_summary.weekday())
            next_week_start_date_summary = week_start_summary + pd.Timedelta(days=7)
            next_week_end_date_summary = next_week_start_date_summary + pd.Timedelta(days=4)  # 5个交易日
            
            print(f"\n📋 推荐总结 (手动测试模式):")
            print(f"   • 测试基准日: {manual_test_date}")
            print(f"   • 推荐范围: {next_week_start_date_summary.strftime('%Y-%m-%d')} 至 {next_week_end_date_summary.strftime('%Y-%m-%d')} (5个交易日)")
            print(f"   • 适用场景: 验证模型在指定日期的预测准确性")
            print(f"   • 测试建议: 记录推荐股票，观察下周实际表现")
            print(f"   • 策略配置: 与workflow完全一致，确保测试结果可比性")
        else:
            # 自动模式
            today = pd.Timestamp.today().normalize()
            next_week_start = today + pd.Timedelta(days=7)
            next_week_end = next_week_start + pd.Timedelta(days=6)
            
            print(f"\n📋 推荐总结:")
            print(f"   • 生成时间: {today.strftime('%Y-%m-%d %H:%M:%S')}")
            print(f"   • 推荐范围: {next_week_start.strftime('%Y-%m-%d')} 至 {next_week_end.strftime('%Y-%m-%d')}")
            print(f"   • 适用场景: 下周交易准备，基于当前模型评分")
            print(f"   • 有效期: 建议在下周内使用，超过一周需重新生成")
        
    except Exception as e:
        print(f"❌ 生成推荐失败: {e}")
        print("\n🔧 故障排除建议:")
        print("1. 确认 mlruns 目录存在且包含训练记录")
        print("2. 尝试手动指定 recorder_id")
        print("3. 检查 qrun 是否完整运行完成")